local E2Helper = { Descriptions = {} }

E2_MAX_ARRAY_SIZE = 1024*1024	// 1MB

local Send = true
local Origin = Vector(0,0,0)
local Destination = Vector(0,0,0)
local TeleportEverything = false

/*---------------------
-- Stargate Fonction --
---------------------*/

registerType("stargate", "g", nil,
	nil,
	function(self,output) return output or NULL end,
	function(retval)
		if retval.IsStargate then return end
		if retval:GetClass() == "ring_base" then return end
		if retval == nil then return end
		if not retval.EntIndex then error("Return value is neither nil nor an Entity, but a "..type(retval).."!",0) end
	end,
	function(v)
		if v.IsStargate then return false end
		if v:GetClass() == "ring_base" then return false end
		return true
	end
)

__e2setcost(5)

e2function stargate array:stargate(idx)
	local ret = this[idx]
	if ret.IsStargate or ret:GetClass() == "ring_base" then return ret end
	return nil
end
e2function stargate array:setStargate(idx, stargate gate)
	if(idx >= E2_MAX_ARRAY_SIZE) then return end
	this[idx] = gate
	self.vclk[this] = true
	return gate
end

--[[registerFunction("stargate", "r:n", "g", function(self, args)
	local op1, op2 = args[2], args[3]
	local rv1, rv2 = op1[1](self, op1), op2[1](self, op2)
	local ret = rv1[rv2]
	if ret.IsStargate or ret:GetClass() == "ring_base" then return ret end
	return nil
end)
registerFunction("setStargate", "r:ng", "g", function(self, args)
	local op1, op2, op3 = args[2], args[3], args[4]
	local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3)
	if(rv2 >= E2_MAX_ARRAY_SIZE) then return end
	rv1[rv2] = rv3
	self.vclk[rv1] = true
	return rv3
end)]]

e2function stargate stargate(entity ent)
	if not ValidEntity(ent) then return nil end
	if not ent.IsStargate and not ent:GetClass() == "ring_base" then return nil end
	return ent
end

e2function stargate stargate(entId)
	local ent = ents.GetByIndex(entId)
	if not ValidEntity(ent) then return nil end
	if not ent.IsStargate and not ent:GetClass() == "ring_base" then return nil end
	return ent
end

__e2setcost(20)

e2function stargate stargate(string address)
	ent = nil
	for _,v in pairs(ents.FindByClass("stargate_*")) do
		if(v.GateAddress == address) then
			ent = v
			break
		end
	end
	if ent == nil then
		for _,v in pairs(ents.FindByClass("ring_base")) do
			if(v.Address == address) then
				ent = v
				break
			end
		end
	end
	return ent
end

e2function array getAllStargates()
	local ret = {}
	for _,v in pairs(ents.FindByClass("stargate_*")) do
		if v.IsStargate then
			table.insert(ret, v)
		end
	end
	return ret
end

e2function array getAllRings()
	local ret = {}
	for _,v in pairs(ents.FindByClass("ring_base")) do
		table.insert(ret, v)
	end
	return ret
end

__e2setcost(5)

e2function number stargate:id()
	if not ValidEntity(this) then return 0 end
	if not this.IsStargate and not this:GetClass() == "ring_base" then return 0 end
	return this:EntIndex()
end

e2function string stargate:name()
	if not ValidEntity(this) then return "" end
	if not this.IsStargate then return "" end
	return this:GetGateName()
end

e2function string stargate:address()
	if not ValidEntity(this) then return "" end
	if this.IsStargate then return this:GetGateAddress() end
	if this:GetClass() == "ring_base" then return this.Address or "" end
	return ""
end

e2function number stargate:private()
	if not this.IsStargate then return 0 end
	if not ValidEntity(this) then return 0 end
	if this:GetPrivate() then return 1 else return 0 end
end

e2function number stargate:range(range)
	if not ValidEntity(this) then return 0 end
	if not this:GetClass() == "ring_base" then return 0 end
	return this.SetRange
end

e2function vector stargate:origin()
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	return { Origin }
end

e2function vector stargate:destination()
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	return { Destination }
end

e2function number stargate:everything()
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	if TeleportEverything then return 1 else return 0 end
end

__e2setcost(10)

e2function number stargate:setName(string name)
	if not ValidEntity(this) then return 0 end
	--if not isOwner(self, this) then return 0 end
	if not this.IsStargate then return 0 end
	this:SetGateName(name)
	return 1
end

e2function number stargate:setAddress(string address)
	if not ValidEntity(this) then return 0 end
	--if not isOwner(self, this) then return 0 end
	if this.IsStargate then
		if (string.len(address) == 6) then
			this:SetGateAddress(address)
			return 1
		else
			return 0
		end
	end
	if this:GetClass() == "ring_base" then
		self.RingNameEnt = this
		RingsNamingCallback( self, "", { address } )
		return 1
	end
	return 0
end

e2function number stargate:setPrivate(private)
	if not ValidEntity(this) then return 0 end
	--if not isOwner(self, this) then return 0 end
	if not this.IsStargate then return 0 end
	if (private == 1) then
		this:SetPrivate(1)
		return 1
	else
		this:SetPrivate(0)
		return 0
	end
end

e2function number stargate:setRange(range)
	if not ValidEntity(this) then return 0 end
	--if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "ring_base" then return 0 end
	this.SetRange = range
	return 1
end

e2function number stargate:setOrigin(vector pos)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	Origin = Vector(pos[1], pos[2], pos[3])
	return 1
end

e2function number stargate:setOrigin(x, y, z)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	Origin = Vector(x, y, z)
	return 1
end

e2function number stargate:setDestination(vector pos)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	Destination = Vector(pos[1], pos[2], pos[3])
	return 1
end

e2function number stargate:setDestination(x, y, z)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	Destination = Vector(x, y, z)
	return 1
end

e2function number stargate:setEverything(all)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	TeleportEverything = util.tobool(all)
	return 1
end

e2function number stargate:send()
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	Send = true
	return 1
end

e2function number stargate:retrieve()
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	Send = false
	return 1
end

__e2setcost(4)

e2function number stargate:active()
	if not ValidEntity(this) then return 0 end
	if this.IsStargate then
		if util.tobool(this.Active) then return 1 else return 0 end
	end
	if this:GetClass() == "ring_base" then return this.Busy end
	return 0
end

e2function number stargate:open()
	if not ValidEntity(this) then return 0 end
	if not this.IsStargate then return 0 end
	if util.tobool(this.IsOpen) then return 1 else return 0 end
end

e2function number stargate:inbound()
	if not ValidEntity(this) then return 0 end
	if not this.IsStargate then return 0 end
	if util.tobool(this.Active and not this.Outbound) then return 1 else return 0 end
end

--[[e2function number stargate:chevron()
	if not ValidEntity(this) then return 0 end
	if not this.IsStargate then return 0 end
	return this.ActivateChevron
end]]

e2function number stargate:usable()
	if not ValidEntity(this) then return 0 end
	if not this:GetClass() == "ring_base" then return 0 end
	return not this.Busy
end

e2function stargate stargate:target()
	if not ValidEntity(this) then return nil end
	if not this.IsStargate then return nil end
	return this.Target
end

e2function iris stargate:iris()
	if not ValidEntity(this) then return nil end
	if not this.IsStargate then return nil end
	for _,v in pairs(ents.FindInSphere(this:GetPos(), 10)) do
		if v.IsIris then
			return v
		end
	end
	return nil
end

e2function number stargate:distance()
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	return Destination:Distance(Origin)
end

e2function number stargate:distance(vector origin, vector destination)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	local destination = Vector(destination[1], destination[2], destination[3])
	local origin = Vector(origin[1], origin[2], origin[3])
	return destination:Distance(origin)
end

e2function number stargate:distance(originX, originY, originZ, destinationX, destinationY, destinationZ)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	local destination = Vector(destinationX, destinationY, destinationZ)
	local origin = Vector(originX, originY,  originZ)
	return destination:Distance(origin)
end

__e2setcost(10)

e2function void stargate:dialClosest()
	if not ValidEntity(this) then return end
	if not this:GetClass() == "ring_base" then return end
	if not this.Busy then this:Dial("") end
end

e2function void stargate:dial(string addess)
	if not ValidEntity(this) then return end
	if this.IsStargate then
		if this.Dialling and this.Outbound and not this.IsOpen then this:AbortDialling() else this:DialGate(addess, true) end
	end
	if this:GetClass() == "ring_base" then
		if util.tobool(addess) and not this.Busy then this:Dial(addess) end
	end
end

e2function void stargate:dial(string addess, mode)
	if not ValidEntity(this) then return end
	if not this.IsStargate then return end
	if this.Dialling and this.Outbound and not this.IsOpen then this:AbortDialling() else this:DialGate(addess, util.tobool(mode)) end
end

e2function void stargate:dial(entity ent)
	if not ValidEntity(this) then return end
	if this.IsStargate then
		if this.Dialling and this.Outbound and not this.IsOpen then this:AbortDialling() else this:DialGate(ent:GetGateAddress(), true) end
	end
	if this:GetClass() == "ring_base" then
		if util.tobool(ent.Address) and not this.Busy then this:Dial(ent.Address) end
	end
end

e2function void stargate:dial(entity stargate, mode)
	if not ValidEntity(this) then return end
	if not this.IsStargate then return end
	if this.Dialling and this.Outbound and not this.IsOpen then this:AbortDialling() else this:DialGate(stargate:GetGateAddress(), util.tobool(mode)) end
end

e2function void stargate:close()
	if not ValidEntity(this) then return end
	if not this.IsStargate then return end
	this:AbortDialling()
end

e2function number stargate:findObjects()
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	if Send then local position = Origin else local position = Destination end
	this.TeleportEverything = util.tobool(TeleportEverything)
	return table.Count(this:FindObjects(position), true)
end

e2function number stargate:findObjects(vector pos, everything)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	this.TeleportEverything = util.tobool(everything)
	return table.Count(this:FindObjects(Vector(pos[1], pos[2], pos[3]), true))
end

e2function number stargate:findObjects(posX, posY, posZ, everything)
	if not ValidEntity(this) then return 0 end
	if not isOwner(self, this) then return 0 end
	if not this:GetClass() == "transporter" then return 0 end
	this.TeleportEverything = util.tobool(everything)
	return table.Count(this:FindObjects(Vector(posX, posY, posZ), true))
end

e2function void stargate:teleport()
	if not ValidEntity(this) then return end
	if not isOwner(self, this) then return end
	if not this:GetClass() == "transporter" then return end
	this.TeleportEverything = TeleportEverything
	if Send then this:Teleport(Origin, Destination) else this:Teleport(Destination, Origin) end
end

e2function void stargate:teleport(vector origin, vector destination, everything)
	if not ValidEntity(this) then return end
	if not isOwner(self, this) then return end
	if not this:GetClass() == "transporter" then return end
	this.TeleportEverything = util.tobool(everything)
	this:Teleport(Vector(origin[1], origin[2], origin[3]), Vector(destination[1], destination[2], destination[3]))
end

e2function void stargate:teleport(originX, originY, originZ, destinationX, destinationY, destinationZ, everything)
	if not ValidEntity(this) then return end
	if not isOwner(self, this) then return end
	if not this:GetClass() == "transporter" then return end
	this.TeleportEverything = util.tobool(everything)
	this:Teleport(Vector(originX, originY, originZ), Vector(destinationX, destinationY, destinationZ))
end

/*-----------------
-- Iris Fonction --
-----------------*/

registerType("iris", "i", nil,
	nil,
	function(self,output) return output or NULL end,
	function(retval)
		if retval.IsIris then return end
		if retval == nil then return end
		if not retval.EntIndex then error("Return value is neither nil nor an Entity, but a "..type(retval).."!",0) end
	end,
	function(v)
		return not v.IsIris
	end
)

__e2setcost(5)

e2function iris array:iris(idx)
	local ret = this[idx]
	if ret.IsIris then return ret end
	return nil
end
e2function iris array:setIris(idx, iris iris)
	if(idx >= E2_MAX_ARRAY_SIZE) then return end
	this[idx] = iris
	self.vclk[this] = true
	return iris
end

--[[registerFunction("iris", "r:n", "i", function(self, args)
	local op1, op2 = args[2], args[3]
	local rv1, rv2 = op1[1](self, op1), op2[1](self, op2)
	local ret = rv1[rv2]
	if this.IsIris then return ret end
	return nil
end)
registerFunction("setIris", "r:ni", "i", function(self, args)
	local op1, op2, op3 = args[2], args[3], args[4]
	local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3)
	if(rv2 >= E2_MAX_ARRAY_SIZE) then return end
	rv1[rv2] = rv3
	self.vclk[rv1] = true
	return rv3
end)]]

__e2setcost(10)

e2function iris iris(entity ent)
	if not ValidEntity(ent) then return nil end
	if not ent.IsIris then return nil end
	return ent
end

e2function iris iris(entId)
	local ent = ents.GetByIndex(entId)
	if not ValidEntity(ent) then return nil end
	if not ent.IsIris then return nil end
	return ent
end

__e2setcost(5)

e2function number iris:id()
	if not ValidEntity(this) then return 0 end
	if not this.IsIris then return 0 end
	return this:EntIndex()
end

__e2setcost(4)

e2function number iris:active()
	if not ValidEntity(this) then return end
	if not this.IsIris then return end
	if not self.IsActivated then return 1 else return 0 end
end

__e2setcost(10)

--[[e2function void iris:open()
	if not ValidEntity(this) then return end
	if not this.IsIris then return end
	if self.IsActivated then this:Toggle() end
end

e2function void iris:close()
	if not ValidEntity(this) then return end
	if not this.IsIris then return end
	if not self.IsActivated then this:Toggle() end
end]]

e2function void iris:toggle()
	if not ValidEntity(this) then return end
	if not this.IsIris then return end
	this:Toggle()
end

__e2setcost(nil)